﻿/**
 * Copyright © TnT-VNGroup 2011
 * PROJECT:   SmartCollector
 * $HeadURL: http://smartcollector.googlecode.com/svn/branches/Srcs/SmartCollector.GUI/CollectorForm.cs $
 * $Author: chithanh12@gmail.com $
 * $Date: 2011-05-13 16:47:25 +0000 (Fri, 13 May 2011) $
 * $Rev: 120 $
 **/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SmartCollector.Domain.Repository;
using System.Data.Linq;
using SmartCollector.Domain;
using HtmlParser.SiteManager;
using HtmlParser;
using SmartCollector.Domain.Entity;
using System.Globalization;
using System.Threading;

namespace SmartCollector
{
    public partial class CollectorForm : Form
    {
        #region Constant FIELDS
        public const string ADD_SITE = "Add New Site";
        public const string ADD_LINKRSS = "Add New LinkRss";
        public const string ADD_CATEGORY = "Add New Category";
        public const string REMOVE_SITE = "Remove Site";
        public const string REMOVE_CATEGORY = "Remove Category";
        public const string EDIT_CATEGORIES = "Edit Category";
        public const string REMOVE_LINKRSS = "Remove LinkRss";
        public const string CATEGORIES = "Categories";
        #endregion

        #region Members
        private SiteRepository m_siteRepository;
        private ItemRepository m_itemRepository;
        private LinkRssRepository m_linkRssRepository;
        private CategoryRepository m_categoryRepository;

        private ContextMenu m_contextMenu = new ContextMenu();
        private MenuItem m_mnuItemAddSite;
        private MenuItem m_mnuItemAddLinkRss;
        private MenuItem m_mnuItemRemoveLinkRss;
        private MenuItem m_mnuItemRemoveSite;
        private MenuItem m_mnuItemAddCategory;
        private MenuItem m_mnuItemRemoveCategory;
        private MenuItem m_mnuCategories;
        private MenuItem m_mnuEditCategory;
        private RssReader.RssReader m_rssReader = new SmartCollector.RssReader.RssReader();

        private BackgroundWorker m_newBackgroundWoker;
        private int m_countNewItem = 0;
        private ViewSelectedView m_currentSelectedView = ViewSelectedView.Sites;
        private List<TreeNode> m_linkRssCollection = new List<TreeNode>();
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the current site structure.
        /// </summary>
        /// <value>
        /// The current site structure.
        /// </value>
        public SiteItemStructure CurrentSiteStructure
        {
            get;
            set;
        }

        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="CollectorForm"/> class.
        /// </summary>
        public CollectorForm()
        {
            InitializeComponent();
            m_siteRepository = new SiteRepository();
            m_itemRepository = new ItemRepository();
            m_linkRssRepository = new LinkRssRepository();
            m_categoryRepository = new CategoryRepository();
            FilterManager.FilterManager.Instance.ItemRepository = m_itemRepository;

            m_newBackgroundWoker = new BackgroundWorker();
            m_newBackgroundWoker.WorkerSupportsCancellation = true;
            m_newBackgroundWoker.WorkerReportsProgress = true;
            m_newBackgroundWoker.DoWork += OnStartCollecting;
            m_newBackgroundWoker.RunWorkerCompleted += OnCollectingCompleted;
            m_newBackgroundWoker.ProgressChanged += OnProcessChanged;

            #region Context Menu
            m_mnuItemAddSite = new MenuItem(ADD_SITE);
            m_mnuItemAddSite.Click += new EventHandler(OnAddNewSiteClick);
            m_contextMenu.MenuItems.Add(m_mnuItemAddSite);

            m_mnuItemRemoveSite = new MenuItem(REMOVE_SITE);
            m_mnuItemRemoveSite.Click += new EventHandler(OnRemoveSite);
            m_contextMenu.MenuItems.Add(m_mnuItemRemoveSite);

            m_mnuItemAddLinkRss = new MenuItem(ADD_LINKRSS);
            m_mnuItemAddLinkRss.Click += new EventHandler(OnAddNewLinkRss);
            m_contextMenu.MenuItems.Add(m_mnuItemAddLinkRss);

            m_mnuItemRemoveLinkRss = new MenuItem(REMOVE_LINKRSS);
            m_mnuItemRemoveLinkRss.Click += new EventHandler(RemoveLinkRss);
            m_contextMenu.MenuItems.Add(m_mnuItemRemoveLinkRss);

            m_mnuCategories = new MenuItem(CATEGORIES);
            m_contextMenu.MenuItems.Add(m_mnuCategories);

            m_mnuItemAddCategory = new MenuItem(ADD_CATEGORY);
            m_mnuItemAddCategory.Click += new EventHandler(OnAddNewCategory);
            m_mnuCategories.MenuItems.Add(m_mnuItemAddCategory);

            m_mnuItemRemoveCategory = new MenuItem(REMOVE_CATEGORY);
            m_mnuItemRemoveCategory.Click += new EventHandler(OnRemoveCategory);
            m_mnuCategories.MenuItems.Add(m_mnuItemRemoveCategory);

            m_mnuEditCategory = new MenuItem(EDIT_CATEGORIES);
            m_mnuEditCategory.Click += new EventHandler(OnEditCategory);
            m_mnuCategories.MenuItems.Add(m_mnuEditCategory);
            
            #endregion

            //REMOVE WHEN DEPLOY
            LoadDevModule();

        }

        void OnEditCategory(object sender, EventArgs e)
        {
            TreeNode selectedNode = m_mainTreeView.SelectedNode;
            if (selectedNode != null)
            {
                NodeInfo nodeInfo = selectedNode.Tag as NodeInfo;
                if (nodeInfo != null && nodeInfo.Level == TreeNodeLevel.Category)
                {
                    SmartCollector.Controls.AddCategory addForm = new Controls.AddCategory(m_categoryRepository,nodeInfo.CategoryId);
                    DialogResult dialogResult = addForm.ShowDialog();
                    if (dialogResult == System.Windows.Forms.DialogResult.OK)
                    {
                        m_siteRepository.Refresh();
                        LoadTreeSites();
                    }
                }
            }
        }

        /// <summary>
        /// Called when [remove category].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnRemoveCategory(object sender, EventArgs e)
        {
            TreeNode selectedNode = m_mainTreeView.SelectedNode;
            if(selectedNode !=null)
            {
                NodeInfo selectedNodeInfo = selectedNode.Tag as NodeInfo;
                if (selectedNodeInfo != null && selectedNodeInfo.Level == TreeNodeLevel.Category)
                {
                    if(MessageBox.Show(string.Format("Are you sure to delete category: {0} and all of Rss"
                        +", Items of that category?",selectedNode.Text),"Remove confirmation", 
                        MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                    {
                        //Remove all Rss
                        m_linkRssRepository.RemoveLinkRss(selectedNodeInfo.CategoryId);
                        m_linkRssRepository.SubmitChanged();
                        
                        //Remove all Items
                        m_itemRepository.RemoveItemsbyCategory(selectedNodeInfo.CategoryId);
                        m_itemRepository.SubmitChanges();

                        //Remove this category
                        m_categoryRepository.RemoveCategory(selectedNodeInfo.CategoryId);
                        m_categoryRepository.SubmitChanged();

                        LoadTreeSites();
                    }
                }
            }
            
        }

        /// <summary>
        /// Called when [add new category].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnAddNewCategory(object sender, EventArgs e)
        {
            SmartCollector.Controls.AddCategory addForm = new Controls.AddCategory(m_categoryRepository);
            DialogResult dialogResult = addForm.ShowDialog();
            if (dialogResult == System.Windows.Forms.DialogResult.OK)
            {
                m_siteRepository.Refresh();
                LoadTreeSites();
            }
        }

        /// <summary>
        /// Removes the link RSS.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void RemoveLinkRss(object sender, EventArgs e)
        {
            NodeInfo selectedNodeInfo = m_mainTreeView.SelectedNode.Tag as NodeInfo;
            DialogResult confirmResult = MessageBox.Show(
                string.Format("Are you sure to delete LinkRss: \n{0}", m_mainTreeView.SelectedNode.Text)
                , "Confirm", MessageBoxButtons.YesNo);
            if (confirmResult == System.Windows.Forms.DialogResult.Yes)
            {
                m_linkRssRepository.RemoveLinkRss(selectedNodeInfo.SiteId, selectedNodeInfo.CategoryId);
                m_linkRssRepository.SubmitChanged();
                m_siteRepository.Refresh();
                LoadTreeSites();
            }
        }

        /// <summary>
        /// Called when [add new link RSS].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnAddNewLinkRss(object sender, EventArgs e)
        {
            NodeInfo selectedNodeInfo = m_mainTreeView.SelectedNode.Tag as NodeInfo;
            if (selectedNodeInfo == null)
            {
                return;
            }
            SmartCollector.Controls.AddLinkRss addForm = new Controls.AddLinkRss(
                m_linkRssRepository, m_siteRepository, m_categoryRepository, selectedNodeInfo.SiteId);
            DialogResult dialogResult = addForm.ShowDialog();
            if (dialogResult == System.Windows.Forms.DialogResult.OK)
            {
                m_siteRepository.Refresh();
                LoadTreeSites();
            }
        }
        #endregion

        #region Methods

        #region ContextMenu
        #endregion

        #region Main Tab

        /// <summary>
        /// Handles the Load event of the CollectorForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void CollectorForm_Load(object sender, EventArgs e)
        {

            LoadTreeSites();
            InitSites();
        }

        /// <summary>
        /// Loads the site tree.
        /// </summary>
        private void LoadTreeSites()
        {
            m_mainTreeView.Nodes.Clear();
            m_linkRssCollection.Clear();
            TreeNode root = new TreeNode(m_currentSelectedView.ToString());
            root.Tag = new NodeInfo { Level = TreeNodeLevel.Root };

            if (m_currentSelectedView == ViewSelectedView.Categories)
            {
                // View by Category -> Sites -> Rss
                foreach (var cate in m_categoryRepository.Categories.Where(p => !p.ParentID.HasValue))
                {
                    TreeNode cateNode = ConstructTreeNode(cate, true);
                    // Check Childs
                    var childsCategory = m_categoryRepository.Categories.Where(c => c.ParentID == cate.ID);
                    foreach (var child in childsCategory)
                    {
                        cateNode.Nodes.Add(ConstructTreeNode(child, false));
                    }
                    root.Nodes.Add(cateNode);
                }
            }
            else
            {
                foreach (var site in m_siteRepository.Sites)
                {
                    root.Nodes.Add(ConstructTreeNode(site));
                }
            }

            #region Old Codes
            //foreach (var siteItem in m_siteRepository.Sites)
            //{
            //    TreeNode siteNode = new TreeNode(siteItem.SiteName);
            //    siteNode.Tag = new NodeInfo { SiteId = siteItem.ID, Level = TreeNodeLevel.Site };
            //    foreach (var rss in siteItem.tbl_LinkRSSes)
            //    {
            //        TreeNode rssNode = new TreeNode(rss.LinkRSS);
            //        rssNode.Tag = new NodeInfo
            //        {
            //            Level = TreeNodeLevel.Rss,
            //            CategoryId = (int)rss.CategoryId,
            //            SiteId = siteItem.ID
            //        };

            //        siteNode.Nodes.Add(rssNode);
            //    }
            //    root.Nodes.Add(siteNode);
            //}
            #endregion

            root.Expand();
            m_mainTreeView.Nodes.Add(root);
        }

        /// <summary>
        /// Constructs the tree node.
        /// </summary>
        /// <param name="cate">The cate.</param>
        /// <returns></returns>
        private TreeNode ConstructTreeNode(tbl_Category cate, bool isParent)
        {
            Dictionary<int, TreeNode> siteNodesDictionary = new Dictionary<int, TreeNode>();
            TreeNode cateNode = new TreeNode(cate.CategoryName);
            cateNode.Tag = new NodeInfo
            {
                Level = TreeNodeLevel.Category,
                CategoryId = cate.ID
            };
            TreeNode virtualNode = new TreeNode("DirectLinkRss");
            virtualNode.Tag = new NodeInfo
            {
                Level = TreeNodeLevel.Virtual
            };
            if (isParent && cate.tbl_LinkRSSes.Count>0)
            {
                cateNode.Nodes.Add(virtualNode);
            }
            //Add Link cua chinh category nay
            foreach (var linkRss in cate.tbl_LinkRSSes)
            {
                TreeNode parentNode;
                if (!siteNodesDictionary.TryGetValue(linkRss.SiteReferenceId.Value, out parentNode))
                {
                    parentNode = new TreeNode(linkRss.tbl_ReferenceSite.SiteName);
                    parentNode.Tag = new NodeInfo
                    {
                        Level = TreeNodeLevel.Site,
                        SiteId = linkRss.SiteReferenceId.Value
                    };
                    siteNodesDictionary.Add(linkRss.SiteReferenceId.Value, parentNode);
                    if (isParent)
                    {
                        virtualNode.Nodes.Add(parentNode);
                    }
                    else 
                    { 
                        cateNode.Nodes.Add(parentNode); 
                    }

                }
                TreeNode rssNode = new TreeNode(linkRss.LinkRSS);
                m_linkRssCollection.Add(rssNode);
                rssNode.Tag = new NodeInfo
                {
                    Level = TreeNodeLevel.Rss,
                    CategoryId = linkRss.CategoryId.Value,
                    SiteId = linkRss.SiteReferenceId.Value
                };
                parentNode.Nodes.Add(rssNode);
            }

            return cateNode;
        }

        /// <summary>
        /// Constructs the tree node.
        /// </summary>
        /// <param name="cate">The cate.</param>
        /// <returns></returns>
        private TreeNode ConstructTreeNode(tbl_ReferenceSite site)
        {
            Dictionary<int, TreeNode> categoryNodesDictionary = new Dictionary<int, TreeNode>();
            TreeNode siteNode = new TreeNode(site.SiteName);
            siteNode.Tag = new NodeInfo
            {
                Level = TreeNodeLevel.Site,
                SiteId = site.ID
            };

            //Add Link cua chinh category nay
            foreach (var linkRss in site.tbl_LinkRSSes)
            {
                TreeNode categoryNode;
                if (!categoryNodesDictionary.TryGetValue(linkRss.CategoryId.Value, out categoryNode))
                {
                    tbl_Category category = linkRss.tbl_Category;
                    categoryNode = new TreeNode(category.CategoryName);
                    categoryNode.Tag = new NodeInfo
                    {
                        Level = TreeNodeLevel.Category,
                        SiteId = category.ID
                    };
                    categoryNodesDictionary.Add(category.ID, categoryNode);
                    // Parent Category
                    if (category.ParentID.HasValue)
                    {
                        TreeNode parentCate;
                        if (!categoryNodesDictionary.TryGetValue(category.ParentID.Value, out parentCate))
                        {
                            parentCate = new TreeNode(category.tbl_Category1.CategoryName);
                            parentCate.Tag = new NodeInfo
                            {
                                Level = TreeNodeLevel.Category,
                                CategoryId = category.ParentID.Value
                            };
                            categoryNodesDictionary.Add(category.ParentID.Value, parentCate);
                            siteNode.Nodes.Add(parentCate);
                        }
                        parentCate.Nodes.Add(categoryNode);
                    }
                    else
                    {
                        siteNode.Nodes.Add(categoryNode);
                    }

                }
                TreeNode rssNode = new TreeNode(linkRss.LinkRSS);
                m_linkRssCollection.Add(rssNode);
                rssNode.Tag = new NodeInfo
                {
                    Level = TreeNodeLevel.Rss,
                    CategoryId = linkRss.CategoryId.Value,
                    SiteId = linkRss.SiteReferenceId.Value
                };
                categoryNode.Nodes.Add(rssNode);
            }

            return siteNode;
        }
        /// <summary>
        /// Handles the Click event of the m_bntStartCollecting control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnItemCollecting(object sender, EventArgs e)
        {
            m_progressBar.Visible = true;
            if (!m_newBackgroundWoker.IsBusy)
            {
                m_newBackgroundWoker.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Reads the feed.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="categoryId">The category id.</param>
        /// <param name="siteStructure">The site structure.</param>
        private void ReadFeed(string url, int categoryId, SiteItemStructure siteStructure)
        {
            m_rssReader.FeedLoaded += OnRssFeedLoaded;
            m_rssReader.ItemAdded += OnItemAdded;
            RssReader.RssFeed feed = m_rssReader.Retrieve(url);

            int percent = 0;
            m_newBackgroundWoker.ReportProgress(0);

            foreach (RssReader.RssItem item in feed.Items)
            {
                string dateTime = !string.IsNullOrEmpty(item.Pubdate) ?
                    item.Pubdate : feed.PubDate;
                DateTime time;
                if (!DateTime.TryParse(dateTime, out time))
                {
                    CultureInfo vietNameInfo = CultureInfo.CreateSpecificCulture("vi-VN");
                    if (DateTime.TryParse(dateTime, vietNameInfo, DateTimeStyles.None, out time)) { };
                }
                if (!m_itemRepository.IsExist(categoryId, siteStructure.SiteId, time))
                {
                    ReadNewItemFromRss(item, categoryId, siteStructure, time);
                }
                percent++;

                m_newBackgroundWoker.ReportProgress((percent * 100 / feed.Items.Count));
            }
            m_itemRepository.SubmitChanges();
        }

        /// <summary>
        /// Gets the name of the category.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        private string GetCategoryName(int id)
        {
            //TODO: Implement this correctly
            return id.ToString();
        }

        /// <summary>
        /// Filters the new item.
        /// </summary>
        /// <param name="rssItem">The RSS item.</param>
        /// <param name="categoryId">The category id.</param>
        /// <param name="siteStructure">The site structure.</param>
        private void ReadNewItemFromRss(RssReader.RssItem rssItem, int categoryId,
            SiteItemStructure siteStructure, DateTime time)
        {
            tbl_Item item = FilterManager.FilterManager.Instance.GetNewItem(rssItem, categoryId, siteStructure, time);
            m_countNewItem += 1;
            if (IsValidItem(item))
            {
                m_itemRepository.AddItem(item);
            }
        }
        private bool IsValidItem(tbl_Item item)
        {
            return item.Title.Trim().Length > 0 &&
                item.Description.Trim().Length > 0 &&
                item.Content.Trim().Length > 0;
        }
        /// <summary>
        /// Called when [RSS feed loaded].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnRssFeedLoaded(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Saves the item to XML.
        /// </summary>
        /// <param name="item">The item.</param>
        private void SaveItemToXml(tbl_Item item)
        {
            //SmartCollector.GUI.Helpers.DeSerializeHelper.Deserialize(item,string.Format("Items/{0}.xml",s_filename++));
            //SmartCollector.GUI.Helpers.DeSerializeHelper.SaveNewItemToFile(string.Format("Items/{0}.html", s_filename++), item);
        }

        /// <summary>
        /// Called when [item added].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnItemAdded(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Handles the Resize event of the CollectorForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void CollectorForm_Resize(object sender, EventArgs e)
        {
            tabControl1.Refresh();
        }

        #endregion Main Tab

        #region Config Page Tab

        /// <summary>
        /// Loads the sites.
        /// </summary>
        private void InitSites()
        {
            m_cmbSites.DataSource = m_siteRepository.Sites.ToList();
            m_cmbSites.DisplayMember = "SiteName";
        }

        /// <summary>
        /// Called when [site changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnSiteChanged(object sender, EventArgs e)
        {
            var selectedItem = m_cmbSites.SelectedItem as tbl_ReferenceSite;
            if (selectedItem != null)
            {
                m_lblCurrentSite.Text = string.Format("Site : {0}", selectedItem.SiteName);
                LoadSiteConfig(selectedItem.ID, selectedItem.Url);
            }
        }

        /// <summary>
        /// Loads the site config.
        /// </summary>
        /// <param name="siteId">The site id.</param>
        private void LoadSiteConfig(int siteId, string siteName)
        {
            if (CurrentSiteStructure != null && CurrentSiteStructure.IsDirty)
            {
                SiteStructureManager.Instance.RegisterNewSiteStructure(CurrentSiteStructure);
            }
            m_treeSiteStructure.Nodes.Clear();
            SiteItemStructure oldStructure =
                SiteStructureManager.Instance.GetSiteItemStructure(m_siteRepository.Sites
                        .Where(p => p.ID == siteId)
                        .Select(c => c.Url).FirstOrDefault());
            if (oldStructure != null)
            {
                CurrentSiteStructure = oldStructure;
            }
            else
            {
                CurrentSiteStructure = new SiteItemStructure()
                {
                    SiteId = siteId,
                    SiteDomainName = siteName,
                };
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Refreshes the sites structure tree.
        /// </summary>
        private void RefreshSitesStructureTree()
        {
            m_treeSiteStructure.Nodes.Clear();
            if (CurrentSiteStructure == null)
            {
                return;
            }
            TreeNode siteNode = new TreeNode(CurrentSiteStructure.SiteDomainName);
            foreach (RegionArea region in Enum.GetValues(typeof(RegionArea)))
            {
                TreeNode regionNode = new TreeNode(region.ToString());
                TagSelector selector = CurrentSiteStructure.GetConstraint(region);
                if (selector != null)
                {
                    foreach (KeyValuePair<string, string> constraint in selector.ConstraintCollections)
                    {
                        regionNode.Nodes.Add(
                            new TreeNode(string.Format("{0} : {1}", constraint.Key, constraint.Value)));
                    }
                }
                siteNode.Nodes.Add(regionNode);
            }
            siteNode.ExpandAll();
            m_treeSiteStructure.Nodes.Add(siteNode);
        }

        /// <summary>
        /// Called when [add boundary structure].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnAddBoundaryStructure(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(m_txtBoundaryKey.Text.Trim())
                || string.IsNullOrEmpty(m_txtBoundaryValue.Text.Trim()))
            {
                MessageBox.Show("Invalid Input!");
                return;
            }
            if (CurrentSiteStructure != null)
            {
                CurrentSiteStructure.AddConstraint(RegionArea.Boundary,
                    m_txtBoundaryKey.Text.Trim(),
                    m_txtBoundaryValue.Text.Trim());
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Adds the title structure.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddTitleStructure(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(m_txtTitleKey.Text.Trim())
                || string.IsNullOrEmpty(m_txtTitleValue.Text.Trim()))
            {
                MessageBox.Show("Invalid Input!");
                return;
            }
            if (CurrentSiteStructure != null)
            {
                CurrentSiteStructure.AddConstraint(RegionArea.Title,
                    m_txtTitleKey.Text.Trim(),
                    m_txtTitleValue.Text.Trim());
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Adds the description structure.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddDescriptionStructure(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(m_txtDescriptionKey.Text.Trim())
                || string.IsNullOrEmpty(m_txtDescriptionValue.Text.Trim()))
            {
                MessageBox.Show("Invalid Input!");
                return;
            }
            if (CurrentSiteStructure != null)
            {
                CurrentSiteStructure.AddConstraint(RegionArea.Description,
                    m_txtDescriptionKey.Text.Trim(),
                    m_txtDescriptionValue.Text.Trim());
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Adds the content struture.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddContentStruture(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(m_txtContentKey.Text.Trim())
                || string.IsNullOrEmpty(m_txtContentValue.Text.Trim()))
            {
                MessageBox.Show("Invalid Input!");
                return;
            }
            if (CurrentSiteStructure != null)
            {
                CurrentSiteStructure.AddConstraint(RegionArea.Content,
                    m_txtContentKey.Text.Trim(),
                    m_txtContentValue.Text.Trim());
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Handles the Click event of the m_bntAddAuthor control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void m_bntAddAuthor_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(m_txtAuthorKey.Text.Trim())
                || string.IsNullOrEmpty(m_txtAuthorValue.Text.Trim()))
            {
                MessageBox.Show("Invalid Input!");
                return;
            }
            if (CurrentSiteStructure != null)
            {
                CurrentSiteStructure.AddConstraint(RegionArea.Author,
                    m_txtAuthorKey.Text.Trim(),
                    m_txtAuthorValue.Text.Trim());
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Handles the Click event of the m_bntAddSource control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void m_bntAddSource_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(m_txtSourceKey.Text.Trim())
                || string.IsNullOrEmpty(m_txtSourceValue.Text.Trim()))
            {
                MessageBox.Show("Invalid Input!");
                return;
            }
            if (CurrentSiteStructure != null)
            {
                CurrentSiteStructure.AddConstraint(RegionArea.Source,
                    m_txtSourceKey.Text.Trim(),
                    m_txtSourceValue.Text.Trim());
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Handles the Click event of the m_bntAddNearestBound control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void m_bntAddNearestBound_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(m_txtNearestKey.Text.Trim())
                || string.IsNullOrEmpty(m_txtNearestValue.Text.Trim()))
            {
                MessageBox.Show("Invalid Input!");
                return;
            }
            if (CurrentSiteStructure != null)
            {
                CurrentSiteStructure.AddConstraint(RegionArea.NearestBoundary,
                    m_txtNearestKey.Text.Trim(),
                    m_txtNearestValue.Text.Trim());
            }
            RefreshSitesStructureTree();
        }

        /// <summary>
        /// Called when [save site config].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnSaveSiteConfig(object sender, EventArgs e)
        {
            SiteStructureManager.Instance.SaveToXml();
        }

        #endregion

        #region Events

        /// <summary>
        /// Called when [add new site].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnAddNewSite(object sender, EventArgs e)
        {
            SmartCollector.GUI.AddSite addsite = new GUI.AddSite();
            addsite.ShowDialog();
            LoadTreeSites();
            InitSites();
        }

        /// <summary>
        /// Called when [start collecting].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        public void OnStartCollecting(object sender, DoWorkEventArgs e)
        {
            TreeNode root = m_mainTreeView.Nodes[0];
            m_countNewItem = 0;
            foreach (TreeNode rssNode in m_linkRssCollection)
            {
                if (m_newBackgroundWoker.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }
                NodeInfo siteInfo = rssNode.Tag as NodeInfo;
                SiteItemStructure siteStructure = SiteStructureManager.
                    Instance.GetSiteItemStructure(m_siteRepository.Sites
                        .Where(p => p.ID == siteInfo.SiteId)
                        .Select(c => c.Url).FirstOrDefault());

                ReadFeed(rssNode.Text, siteInfo.CategoryId, siteStructure);
                
            }
            //m_progressBar.Visible = false;
            //m_itemRepository.SubmitChanges();
        }

        /// <summary>
        /// Called when [collecting completed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        public void OnCollectingCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            m_progressBar.Visible = false;
            //this.Text = "Collector Finish!" + ":" + "have :" + m_itemRepository.Items.Count() + "tin";
            MessageBox.Show(string.Format("Completed! We get {0} new items", m_countNewItem));
        }

        /// <summary>
        /// Called when [process changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
        public void OnProcessChanged(object sender, ProgressChangedEventArgs e)
        {
            m_progressBar.Value = e.ProgressPercentage;
        }

        /// <summary>
        /// Called when [tree view mouse click].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeNodeMouseClickEventArgs"/> instance containing the event data.</param>
        private void OnTreeViewMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {

            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (m_mainTreeView.SelectedNode != null)
                {
                    NodeInfo currentNodeInfo = m_mainTreeView.SelectedNode.Tag as NodeInfo;
                    UpdateContextMenu(currentNodeInfo.Level);
                    m_contextMenu.Show(m_mainTreeView, e.Location);
                }
            }
        }

        /// <summary>
        /// Updates the context menu.
        /// </summary>
        /// <param name="treeNodeLevel">The tree node level.</param>
        private void UpdateContextMenu(TreeNodeLevel treeNodeLevel)
        {
            m_mnuItemAddLinkRss.Enabled = treeNodeLevel == TreeNodeLevel.Site;
            m_mnuItemAddSite.Enabled = treeNodeLevel == TreeNodeLevel.Root;
            //m_mnuItemAddCategory.Enabled = treeNodeLevel == TreeNodeLevel.Root;

            //m_mnuItemRemoveCategory.Enabled = treeNodeLevel == TreeNodeLevel.Root;
            m_mnuItemRemoveLinkRss.Enabled = treeNodeLevel == TreeNodeLevel.Rss;
            m_mnuItemRemoveSite.Enabled = treeNodeLevel == TreeNodeLevel.Site;

            m_mnuEditCategory.Enabled = treeNodeLevel == TreeNodeLevel.Category;
            m_mnuItemRemoveCategory.Enabled = treeNodeLevel == TreeNodeLevel.Category;
        }

        /// <summary>
        /// Called when [add new site click].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void OnAddNewSiteClick(object sender, EventArgs e)
        {
            SmartCollector.GUI.AddSite addsite = new GUI.AddSite(m_siteRepository);
            DialogResult result = addsite.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                LoadTreeSites();
            }
        }

        /// <summary>
        /// Called when [remove site].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void OnRemoveSite(object sender, EventArgs e)
        {
            if (m_mainTreeView.SelectedNode != null)
            {
                NodeInfo selectedNodeInfo = m_mainTreeView.SelectedNode.Tag as NodeInfo;
                DialogResult confirmResult = MessageBox.Show(string.Format("Are you sure to delete {0}",
                    m_siteRepository.GetSiteNameById(selectedNodeInfo.SiteId)),
                    "Confirm Delete!", MessageBoxButtons.YesNo);
                if (confirmResult == System.Windows.Forms.DialogResult.Yes)
                {
                    m_siteRepository.DeleteSiteById(selectedNodeInfo.SiteId);
                    m_siteRepository.SubmitChanges();
                    LoadTreeSites();
                }
            }
        }

        #endregion

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Form.Closing"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.ComponentModel.CancelEventArgs"/> that contains the event data.</param>
        protected override void OnClosing(CancelEventArgs e)
        {
            if (SiteStructureManager.Instance.IsDirty)
            {
                DialogResult rs = MessageBox.Show("Do you want to save all site Structure Config?", "Confirm Save", MessageBoxButtons.YesNo);
                if (rs == System.Windows.Forms.DialogResult.Yes)
                {
                    SiteStructureManager.Instance.SaveToXml();
                }
            }
            base.OnClosing(e);
        }
        #endregion

        #region Develop Modules
        private void ReadOneItemClick(object sender, EventArgs e)
        {
            SiteItemStructure siteStructure = SiteStructureManager.
                    Instance.GetSiteItemStructure(dev_cmbSites.Text);
            tbl_Item item = FilterManager.FilterManager.Instance.GetNewItem(
                dev_txtItemLink.Text, 1, siteStructure); 
            item.Date = new DateTime(2011,4,23);
            
            //m_itemRepository.AddItem(item);
            //m_itemRepository.SubmitChanges();
            MessageBox.Show(string.Format("Get 1 Items from Url: {0}",item.OriginalUrl));
        }   

        private void LoadDevModule()
        {
            dev_cmbSites.DataSource = m_siteRepository.Sites.OfType<tbl_ReferenceSite>().ToList();
            dev_cmbSites.DisplayMember = "Url";

        }

        #endregion

        /// <summary>
        /// Called when [changed current view].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnChangedCurrentView(object sender, EventArgs e)
        {
            if (m_currentSelectedView == ViewSelectedView.Sites)
            {
                m_currentSelectedView = ViewSelectedView.Categories;
            }
            else
            {
                m_currentSelectedView = ViewSelectedView.Sites;
            }
            LoadTreeSites();
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class NodeInfo
    {
        public int CategoryId
        {
            get;
            set;
        }

        public int SiteId
        {
            get;
            set;
        }
        public TreeNodeLevel Level
        {
            get;
            set;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public enum TreeNodeLevel
    {
        Root,
        Site,
        Rss,
        Category,
        Virtual
    }

    /// <summary>
    /// 
    /// </summary>
    public enum ViewSelectedView
    {
        Sites,
        Categories
    }
}
